#ifndef ACCOUNT_H__
#define ACCOUNT_H__

//include header
#include "AccountState.h"
#include <covariant>
#include <string>

//using namespace
using namespace std;


namespace npState
{
    //class forward declaration
    
    class Account ://  public BaseClass
        public covariant
    {
        //friend
        
        //embedded type
        public: enum class ErrorEnum
            {
                StateIsNull = 1
            };
        
        //class skeleton
        public: Account();
        public: Account(const Account& cpy);
        public: Account(Account&& mov) noexcept;
        public: virtual ~Account();
        public: Account& operator=(const Account& asn);
        public: Account& operator=(Account&& mva) noexcept;
        public: virtual shared_ptr<covariant> clone() const throw();
        public: virtual shared_ptr<covariant> steal(void* sft=nullptr) throw();

        //other constructors
        public: explicit Account(const string& owner, double initBalance = 0);
        
        //public methods
        public: void setOwner(const string& owner) throw();
        public: string getOwner() const throw();
        public: void setBalance(double balance) throw();
        public: double getBalance() const throw();
        public: void setState(shared_ptr<AccountState> state) throw();
        public: void deposit(double amount) throw(ErrorEnum);
        public: void withdraw(double amount) throw(ErrorEnum);
        public: void calcInterest() throw(ErrorEnum);
        
        //protected methods
        
        //private methods
        
        //protected data
        /* 类中不应该存在protected 访问级别的data
         * 要么私有，要么私有而后给出protected 访问级别的methods !!! */
        
        //private data
        /* 类中不应存在指针类型成员，
         * 取而代之，应使用智能指针予以包裹
         * 使用shared_ptr表示类拥有指针指向对象
         * 使用weak_ptr表示类仅可以使用指针指向对象
         * 智能指针对指针成员的封装和使用准则：
         * 1.类对象不允许使用unique_ptr包裹
         * 2.推敲业务逻辑，如果是共享的(share_ptr)，则尽可能少的共享指针对象
         * 3.如果从业务逻辑中发现类仅仅使用某个指针所指对象，则务必使用weak_ptr包裹指针，
         *   以此切实地描述这种使用而不拥有的关系
         * 4.类成员weak_ptr<*> ths，由其产生的，包裹this指针的，shared_ptr只允许在局部使用 */
        private: shared_ptr<AccountState> state;
        private: string owner;
        private: double balance;
        
        //static part
        
        //others
    };

    //inline functions
    inline Account::Account() // : BaseClass()
    {
        this->state = nullptr;
        this->owner = "";
        this->balance = 0;
    }
    
    inline Account::Account(const Account& cpy) // : BaseClass(cpy)
    {
        this->state = __cvt<AccountState>(cpy.state->clone());
        this->owner = cpy.owner;
        this->balance = cpy.balance;
    }
    
    inline Account::Account(Account&& mov) noexcept // : BaseClass(move(mov))
    {
        this->state = move(mov.state);
        this->owner = move(mov.owner);
        this->balance = mov.balance;
    }
    
    inline Account::~Account()
    {
        
    }
    
    inline Account& Account::operator=(const Account& asn)
    {
        if (this == &asn)
        {
            return *this;
        }
        else
        {
            // BaseClass::operator=(asn);
            
            //To do
            this->state = __cvt<AccountState>(asn.state->clone());
            this->owner = asn.owner;
            this->balance = asn.balance;
            
            return *this;
        }
    }
    
    inline Account& Account::operator=(Account&& mva) noexcept
    {
        if (this == &mva)
        {
            return *this;
        }
        else
        {
            // BaseClass::operator=(move(mva));
            
            //To do
            this->state = move(mva.state);
            this->owner = move(mva.owner);
            this->balance = mva.balance;
            
            return *this;
        }
    }
    
    inline shared_ptr<covariant> Account::clone() const throw()
    {
        //new an instance if not abstract.
        return make_shared<Account>(*this);
    }
    
    inline shared_ptr<covariant> Account::steal(void* sft) throw()
    {
        Account* shf = static_cast<Account*>(sft);
        if (this == shf)
        {
            return shared_ptr<covariant>();
        }
        else
        {
            shared_ptr<Account> ret;
            if (!shf)
            {
                //new an instance if not abstract.
                ret = make_shared<Account>();
                shf = ret.get();
            }
            
            // BaseClass::steal(shf);
            
            //To do
            shf->state = move(this->state);
            shf->owner = move(this->owner);
            shf->balance = this->balance;
            
            return ret;
        }
    }

    inline void Account::setOwner(const string& owner) throw()
    {
        this->owner = owner;
    }

    inline string Account::getOwner() const throw()
    {
        return this->owner;
    }

    inline void Account::setBalance(double balance) throw()
    {
        this->balance = balance;
    }

    inline double Account::getBalance() const throw()
    {
        return this->balance;
    }

    inline void Account::setState(shared_ptr<AccountState> state) throw()
    {
        this->state = state;
    }
} // namespace npState


#endif //ACCOUNT_H__
